import greenfoot.*;  
import java.util.*;
/**
 * The abstract base class for sandbags 
 * 
 * @author Jasper Jans
 */
public abstract class Sandbag extends Actor
{

    private SandBags sandBagsWorld;   
    
    protected int sandFilling = 2000;   // the default amount of sandbag-filling

    private boolean isDropping = false; // sandbag is being dropped from the plane
    private boolean isThrown = false;   // sandbag is thrown by the player actor
    private boolean isBlinking = false; // sandbag is almost empty and is blinking 
    private boolean isNextSandBag = false;  // sandbag is shown as next sandbag to be dropped   
    private boolean isFading = false;   // sandbag is currently in fading sequence
    private boolean removeMe = false;   // if true, the sandbag will be removed on next loop 
    protected boolean allowPickUp = true;   // the player actor is allowed to pick up the sandbag

    private int blinkInterval = 0;  // the blink interval 
    private int thrownFromY = 0;    // the y coordinates the sandbag was thrown from

    
    private Direction throwDirection;   // the direction the sandbag is thrown in 

    private double horizontalSpeed, verticalSpeed, gravity = 0.9;   
    protected String caughtImageFile = "sandbag-small2.png";
    protected String caughtImageFileBlink = "sandbag-small2_blink.png";
    protected String dropSound; 
        
    private long timeStarted = System.currentTimeMillis(); 
    private String imageSequence;
    
    public void setAllowPickUp(boolean allowPickUp)
    {
        this.allowPickUp = allowPickUp;    
    }
    
    public boolean getAllowPickUp()
    {
        return this.allowPickUp;
    }
    
    public boolean getisNextSandBag()
    {
        return this.isNextSandBag;
    }

    protected void flagForRemove()
    {
        removeMe = true;
    }
    
    public Sandbag()
    {
        GreenfootImage img = getImage();
        img.scale(img.getWidth() + 100, img.getHeight() + 100);
        setImage(img); 
    }
    
    /*
     * Checks to see if the sandbag was thrown into the breach
     */
    private Boolean thrownIntoBreach()
    {
        List<Actor> breach = getObjectsInRange(75, Breach.class);
        
        if(breach != null && breach.size() == 1)
        {
            if(((Breach) breach.get(0)).checkSandBagLocation(this)) // thrown into a valid column
            {
                new GreenfootSound("sandbags-score01.mp3").play();
                allowPickUp = false;
                return true;
            }else{  // missed the breach, hits the water                
                new GreenfootSound("sandbags-hitthewater.mp3").play();
                flagForRemove();
            }  
        }
        return false;
    }
    
    /*
     * Check the landing location when the sandbag was thrown, add score if breach was hit
     */
    private void checkLandingAfterThrow()
    {
        if(thrownIntoBreach())
        {
            addScore();
        }
    }
    
    /*
     * Abstract method addScore, functionality exists in derived classes
     */
    protected abstract void addScore();
    

    /*
     * Makes all sandbag-related stuff hapen
     */
    public void act() 
    {
        // in fading sequence
        if(isFading)    
        {
            GreenfootImage img = getImage();
            int transparency = img.getTransparency() - 20;
            img.setTransparency(transparency);
            
            if(transparency <= 19)
            {
                img.setTransparency(0);
                isFading = false;
            }
        }
      
        // the sandbag is almost empty and thus is blinking
        if(getWorld() instanceof MiniGame && ((MiniGame)getWorld()).getGameStatus() == GameStatus.PLAYING &&  isBlinking && blinkInterval > 0)
        {
            long currentTime = System.currentTimeMillis();
            long elapsedTime = currentTime - timeStarted;
            
            if(elapsedTime / 100 >= blinkInterval)
            {  
      
                String nextImage = caughtImageFile;
                if(imageSequence == caughtImageFile)
                {
                    nextImage = caughtImageFileBlink;
                }
                
                imageSequence = nextImage;
                GreenfootImage nextImg = new GreenfootImage(nextImage);
                setImage(nextImg);
                
                timeStarted = System.currentTimeMillis();
            }
        }
        
        // the sandbag has been thrown
        if(isThrown)
        {
            //Air resistance
            horizontalSpeed *= 0.999;
            
            //Gravity
            verticalSpeed -= gravity;
            
            int newX = 0, newY = 0;
            int rot = getRotation();
            
            switch(throwDirection)
            {
                case RIGHT:
                        //Calculate new position
                    newX = getX()+(int)Math.round(horizontalSpeed);
                    newY = getY()-(int)Math.round(verticalSpeed);                
                    rot += 10;
                    setRotation(rot);
                    setLocation(newX, newY);
                    if(newY >= (thrownFromY + 20))
                    {
                        isThrown= false;
                        checkLandingAfterThrow();
                    }
                    break;
                case LEFT:
                    newX = getX()-(int)Math.round(horizontalSpeed);
                    newY = getY()-(int)Math.round(verticalSpeed);                
                    rot -= 10;
                    setRotation(rot);
                    setLocation(newX, newY);
                    if(newY >= (thrownFromY + 20))
                    {
                        isThrown = false;
                        checkLandingAfterThrow();
                    }
                    break;
                
            }  
        }
        
        // The sandbag is dropping out of the plane 
        if(isDropping)
        {
            
            GreenfootImage img = getImage();
            
            if(img.getHeight() <= 30)   // the player is in the location to catch the sandbag
            {
                Actor actor = getOneIntersectingObject(Player.class);
                
                if(actor != null)
                {
                    ((Player)actor).setSandbag(this);
                    isDropping = false;
                    GreenfootImage caughtImage = new GreenfootImage(caughtImageFile);
                    setImage(caughtImage);
                    return;
                }
            }
            
            // decrease size as the sandbag falls 
            img.scale((int)Math.floor( 0.95 * img.getWidth()), (int)Math.floor( 0.95 * img.getHeight()));
            
            setImage(img);
            setRotation(getRotation() + 10);
 
            
            if(img.getHeight() <= 20)
            {
                if(getWorld().getObjectsAt(getX(), getY(), Water.class).size() != 0)
                {
                    flagForRemove();
                }
                stopDropping();
            }
        }
        
        // The sandbag is just laying around and the player actor is walking by the pick it up
        if(!isDropping && !isThrown)
        {
            if(allowPickUp)
            {
                 Actor actor = getOneIntersectingObject(Player.class);
                 
                 if(actor != null) 
                 {
                     Player p = (Player)actor;
                     if(p.getSandbag() == null)
                     {
                         p.setSandbag(this);
                     }
                 }
            }
        }
       
        // remove me when i am finished    
        if(removeMe)
        {
            getWorld().removeObject(this);
        }
           
    }    
    
    /*
     * Display the sandbag in smaller size 
     * This has to been done as the image otherwise would be to blury
     */
    public void small()
    {
        GreenfootImage img = new GreenfootImage(caughtImageFile);
        setImage(img);
        setRotation(340);
    }

    /*
     * The sandbag has hit the ground
     */
    public void stopDropping()
    {
        if(dropSound != null && !dropSound.isEmpty())
        {
            GreenfootSound dropSoundFile = new GreenfootSound(dropSound);
            dropSoundFile.play();   
        }
        
        isDropping = false;
    }
    
    /*
     * Initiate the drop from the airplane
     */
    public void drop() 
    {
        isDropping = true;
        horizontalSpeed = 0;
    }

    /*
     * Called from the player as the throw has been initiated
     */
    public void throwMe(double power, Direction direction)
    {
       throwDirection = direction;
       horizontalSpeed = power;
       verticalSpeed = power;
       isThrown = true;
       thrownFromY = getY(); 
    }
    
    /*
     * Remove a small amount of filling from me, when I am empty, flag me for removal 
     */
    public Boolean emptyMeAndRemove()
    {
        sandFilling -= 75;
        
        setBlinking();
 
        if(sandFilling <= 0)
        {
            flagForRemove();
            return true;
        } 
        
        return false;
    }
    
    /*
     * Start blinking depending on how much filling I have left
     */
    private void setBlinking()
    {
        isBlinking = true;
        
        if(blinkInterval == 0  && sandFilling <= 700)
        {
            blinkInterval = 5;
        }
        
        if(blinkInterval == 5 && sandFilling <= 300)
        {
            blinkInterval = 3;
        }
  
    }
    
    /*
     * Stop the sandbag from blinking
     */
    public void stopBlinking()
    {
        isBlinking = false;
    }
    
    /*
     * Flag me for fading
     */
    public void fade()
    {
        isFading = true;
    }
    
    /*
     * Set the transparency to full
     */
    public void showMe()
    {
        GreenfootImage img = getImage();
        img.setTransparency(255);
 
    }
    
    
}

